package com.example.king.ffmpegexample;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

/**
 * 简单来说码流是这样的byte数组:0001+SPS+0001+PPS+0001+NALU+0001+NALU+…，
 * 解码开始时，需要先把0001+SPS+0001+PPS"喂"给解码器，
 * 然后将后面的一帧数据0001+NALU，继续"喂"给解码器, 然后得到一张解码出来的yuv保存/显示,
 * 再"喂"下一帧数据0001+NALU，得到一张解码出来的yuv, 这样循环直到码流结束．
 *
 * */

public class H264AndroidActivity extends Activity {

    VView1 vv;

    private NativeH264Android nativeH264Android;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        nativeH264Android = new NativeH264Android(Environment.getExternalStorageDirectory()
                + "/test_h264.yuv");

        vv = new VView1(this, nativeH264Android);
        setContentView(vv);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        nativeH264Android.release();
    }

    // Menu item Ids
    public static final int PLAY_ID = Menu.FIRST;
    public static final int EXIT_ID = Menu.FIRST + 1;

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        menu.add(0, PLAY_ID, 0, R.string.play);
        menu.add(0, EXIT_ID, 1, R.string.exit);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case PLAY_ID:
            {
                // 此处设定不同分辨率的码流文件

                String file = Environment.getExternalStorageDirectory() + "/butterfly.h264"; //352x288.264"; //240x320.264";
                vv.PlayVideo(file);

                return true;
            }
            case EXIT_ID:
            {
                finish();
                return true;
            }
        }
        return super.onOptionsItemSelected(item);
    }

}



class VView1 extends View implements Runnable{
    Bitmap mBitQQ  = null;

    Paint mPaint = null;

    Bitmap  mSCBitmap = null;

    int width = 240;  // 此处设定不同的分辨率
    int height = 320;

    byte [] mPixel = new byte[width*height*2];

    ByteBuffer buffer = ByteBuffer.wrap( mPixel );
    Bitmap VideoBit = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

    int mTrans=0x0F0F0F0F;

    String PathFileName;

    private NativeH264Android nativeH264Android;

    public VView1(Context context, NativeH264Android nativeH264Android) {
        super(context);
        setFocusable(true);
        this.nativeH264Android = nativeH264Android;

        int i = mPixel.length;

        for(i=0; i<mPixel.length; i++)
        {
            mPixel[i]=(byte)0x00;
        }
    }

    public void PlayVideo(String file)
    {
        PathFileName = file;

        new Thread(this).start();
    }

    @Override protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

//    	Bitmap tmpBit = Bitmap.createBitmap(mPixel, 320, 480, Bitmap.Config.RGB_565);//.ARGB_8888);

        VideoBit.copyPixelsFromBuffer(buffer);//makeBuffer(data565, N));

        canvas.drawBitmap(VideoBit, 0, 0, null);
    }

    int MergeBuffer(byte[] NalBuf, int NalBufUsed, byte[] SockBuf, int SockBufUsed, int SockRemain)
    {
        int  i=0;
        byte Temp;

        for(i=0; i<SockRemain; i++)
        {
            Temp  = SockBuf[i+SockBufUsed];
            NalBuf[i+NalBufUsed]=Temp;

            mTrans <<= 8;
            mTrans  |= Temp;

            if(mTrans == 1) // 找到一个开始字
            {
                i++;
                break;
            }
        }

        return i;
    }

    public void run()
    {
        InputStream is = null;
        FileInputStream fileIS=null;

        int iTemp=0;
        int nalLen;

        boolean bFirst=true;
        boolean bFindPPS=true;

        int bytesRead=0;
        int NalBufUsed=0;
        int SockBufUsed=0;

        byte [] NalBuf = new byte[40980]; // 40k
        byte [] SockBuf = new byte[2048];

        try
        {
            fileIS = new FileInputStream(PathFileName);
        }
        catch(IOException e)
        {
            e.printStackTrace();
            return ;
        }

        while (!Thread.currentThread().isInterrupted())
        {
            try
            {
                bytesRead = fileIS.read(SockBuf, 0, 2048);
            }
            catch (IOException e) {}

            if(bytesRead<=0) {
                Log.d("kkunion", "-------------bytesRead:"+bytesRead);
                break;
            }

            SockBufUsed =0;

            while(bytesRead-SockBufUsed>0)
            {
                nalLen = MergeBuffer(NalBuf, NalBufUsed, SockBuf, SockBufUsed, bytesRead-SockBufUsed);

                NalBufUsed += nalLen;
                SockBufUsed += nalLen;

                while(mTrans == 1)
                {
                    mTrans = 0xFFFFFFFF;

                    if(bFirst==true) // the first start flag
                    {
                        bFirst = false;
                    }
                    else  // a complete NAL data, include 0x00000001 trail.
                    {
                        if(bFindPPS==true) // true
                        {
                            if( (NalBuf[4]&0x1F) == 7 )
                            {
                                bFindPPS = false;
                            }
                            else
                            {
                                NalBuf[0]=0;
                                NalBuf[1]=0;
                                NalBuf[2]=0;
                                NalBuf[3]=1;

                                NalBufUsed=4;

                                break;
                            }
                        }
                        //	decode nal
//                        iTemp=DecoderNal(NalBuf, NalBufUsed-4, mPixel);
                        iTemp = nativeH264Android.decodeH264ToYuv(NalBuf, NalBufUsed - 4);

//                        if(iTemp>0)
//                            postInvalidate();  //使用postInvalidate可以直接在线程中更新界面    // postInvalidate();
                    }

                    NalBuf[0]=0;
                    NalBuf[1]=0;
                    NalBuf[2]=0;
                    NalBuf[3]=1;

                    NalBufUsed=4;
                }
            }

        }

        try{
            if(fileIS!=null)
                fileIS.close();
            if(is!=null)
                is.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }

    }
}

